package com.sweet.net;

import com.sweet.enums.Charset;
import com.sweet.net.enums.HttpMethod;
import com.sweet.net.enums.ReturnType;
import com.sweet.net.vo.BasicAuthorization;
import com.sweet.net.vo.HttpRequest;
import com.sweet.net.vo.HttpResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpEntity;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.*;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.util.EntityUtils;

import java.util.Map;

/**
 * 封装官方HttpClient类
 * <p>
 * <pre>
 * 	遗留问题：1.使用代理服务器方式访问、2.100-continue问题
 *
 * 	100-continue测试代码 :
 * 	HttpParams params = excute.getParams();
 * 	params.setBooleanParameter(CoreProtocolPNames.USE_EXPECT_CONTINUE, false);
 *
 * 	HttpParams params = http.getParams();
 * 	params.setBooleanParameter(CoreProtocolPNames.USE_EXPECT_CONTINUE, false);
 * </pre>
 *
 * @author 李密
 * @date 2015-09-02
 **/
@Slf4j
public final class HttpClient {
    private static final String CHARSET = Charset.UTF8.getCharset();    //请求编码，默认是utf-8
    public static final int TIME_OUT = 5000;                            //超时时间，单位毫秒，默认5秒

    /**
     * 取消构造，防止使用多例
     */
    private HttpClient() {
    }

    /**
     * GET查询请求
     *
     * @param url 请求地址
     * @return
     */
    public static HttpResponse get(String url) {
        return get(url, false);
    }

    /**
     * GET查询请求
     *
     * @param url      请求地址
     * @param printLog 是否打印日志
     * @return
     */
    public static HttpResponse get(String url, boolean printLog) {
        //定义请求体
        HttpRequest request = new HttpRequest();
        request.setMethod(HttpMethod.GET);
        request.setUrl(url);

        return request(request, printLog);
    }

    /**
     * GET查询请求
     *
     * @param url      请求地址
     * @param timeout  超时时间
     * @param printLog 是否打印日志
     * @return
     */
    public static HttpResponse get(String url, int timeout, boolean printLog) {
        //定义请求体
        HttpRequest request = new HttpRequest();
        request.setMethod(HttpMethod.GET);
        request.setUrl(url);
        request.setTimeout(timeout);

        return request(request, printLog);
    }

    /**
     * GET查询请求
     *
     * @param url       请求地址
     * @param basicAuth basic认证
     * @param timeout   超时时间
     * @param printLog  是否打印日志
     * @return
     */
    public static HttpResponse get(String url, BasicAuthorization basicAuth, int timeout, boolean printLog) {
        //定义请求体
        HttpRequest request = new HttpRequest();
        request.setMethod(HttpMethod.GET);
        request.setUrl(url);
        request.setBasicAuth(basicAuth);
        request.setTimeout(timeout);

        return request(request, printLog);
    }

    /**
     * GET查询请求
     *
     * @param url       请求地址
     * @param headers   请求头
     * @param basicAuth basic认证
     * @param timeout   超时时间
     * @param printLog  是否打印日志
     * @return
     */
    public static HttpResponse get(String url, Map<String, String> headers, BasicAuthorization basicAuth, int timeout
            , boolean printLog) {
        //定义请求体
        HttpRequest request = new HttpRequest();
        request.setMethod(HttpMethod.GET);
        request.setUrl(url);
        request.setHeaders(headers);
        request.setBasicAuth(basicAuth);
        request.setTimeout(timeout);

        return request(request, printLog);
    }

    /**
     * POST添加请求
     *
     * @param url  请求地址
     * @param body body参数
     * @return
     */
    public static HttpResponse post(String url, String body) {
        return post(url, body, false);
    }

    /**
     * POST添加请求
     *
     * @param url      请求地址
     * @param body     body参数
     * @param printLog 是否打印日志
     * @return
     */
    public static HttpResponse post(String url, String body, boolean printLog) {
        //定义请求体
        HttpRequest request = new HttpRequest();
        request.setMethod(HttpMethod.POST);
        request.setUrl(url);
        request.setBody(body);

        return request(request, printLog);
    }

    /**
     * POST添加请求
     *
     * @param url      请求地址
     * @param body     body参数
     * @param timeout  超时时间
     * @param printLog 是否打印日志
     * @return
     */
    public static HttpResponse post(String url, String body, int timeout, boolean printLog) {
        //定义请求体
        HttpRequest request = new HttpRequest();
        request.setMethod(HttpMethod.POST);
        request.setUrl(url);
        request.setBody(body);
        request.setTimeout(timeout);

        return request(request, printLog);
    }

    /**
     * POST添加请求
     *
     * @param url       请求地址
     * @param body      body参数
     * @param basicAuth basic认证
     * @param timeout   超时时间
     * @param printLog  是否打印日志
     * @return
     */
    public static HttpResponse post(String url, String body, BasicAuthorization basicAuth, int timeout,
                                    boolean printLog) {
        //定义请求体
        HttpRequest request = new HttpRequest();
        request.setMethod(HttpMethod.POST);
        request.setUrl(url);
        request.setBody(body);
        request.setBasicAuth(basicAuth);
        request.setTimeout(timeout);

        return request(request, printLog);
    }

    /**
     * POST添加请求
     *
     * @param url       请求地址
     * @param body      body参数
     * @param headers   请求头
     * @param basicAuth basic认证
     * @param timeout   超时时间
     * @param printLog  是否打印日志
     * @return
     */
    public static HttpResponse post(String url, String body, Map<String, String> headers,
                                    BasicAuthorization basicAuth, int timeout, boolean printLog) {
        //定义请求体
        HttpRequest request = new HttpRequest();
        request.setMethod(HttpMethod.POST);
        request.setUrl(url);
        request.setBody(body);
        request.setHeaders(headers);
        request.setBasicAuth(basicAuth);
        request.setTimeout(timeout);

        return request(request, printLog);
    }

    /**
     * PUT修改请求
     *
     * @param url  请求地址
     * @param body body参数
     * @return
     */
    public static HttpResponse put(String url, String body) {
        return put(url, body, false);
    }

    /**
     * PUT修改请求
     *
     * @param url      请求地址
     * @param body     body参数
     * @param printLog 是否打印日志
     * @return
     */
    public static HttpResponse put(String url, String body, boolean printLog) {
        //定义请求体
        HttpRequest request = new HttpRequest();
        request.setMethod(HttpMethod.PUT);
        request.setUrl(url);
        request.setBody(body);

        return request(request, printLog);
    }

    /**
     * PUT修改请求
     *
     * @param url      请求地址
     * @param body     body参数
     * @param timeout  超时时间
     * @param printLog 是否打印日志
     * @return
     */
    public static HttpResponse put(String url, String body, int timeout, boolean printLog) {
        //定义请求体
        HttpRequest request = new HttpRequest();
        request.setMethod(HttpMethod.PUT);
        request.setUrl(url);
        request.setBody(body);
        request.setTimeout(timeout);

        return request(request, printLog);
    }

    /**
     * PUT修改请求
     *
     * @param url       请求地址
     * @param body      body参数
     * @param basicAuth basic认证
     * @param timeout   超时时间
     * @param printLog  是否打印日志
     * @return
     */
    public static HttpResponse put(String url, String body, BasicAuthorization basicAuth, int timeout,
                                   boolean printLog) {
        //定义请求体
        HttpRequest request = new HttpRequest();
        request.setMethod(HttpMethod.PUT);
        request.setUrl(url);
        request.setBody(body);
        request.setBasicAuth(basicAuth);
        request.setTimeout(timeout);

        return request(request, printLog);
    }

    /**
     * PUT修改请求
     *
     * @param url       请求地址
     * @param body      body参数
     * @param headers   请求头
     * @param basicAuth basic认证
     * @param timeout   超时时间
     * @param printLog  是否打印日志
     * @return
     */
    public static HttpResponse put(String url, String body, Map<String, String> headers, BasicAuthorization basicAuth
            , int timeout, boolean printLog) {
        //定义请求体
        HttpRequest request = new HttpRequest();
        request.setMethod(HttpMethod.PUT);
        request.setUrl(url);
        request.setBody(body);
        request.setHeaders(headers);
        request.setBasicAuth(basicAuth);
        request.setTimeout(timeout);

        return request(request, printLog);
    }

    /**
     * DELETE删除请求
     *
     * @param url 请求地址
     * @return
     */
    public static HttpResponse delete(String url) {
        return delete(url, false);
    }

    /**
     * DELETE删除请求
     *
     * @param url      请求地址
     * @param printLog 是否打印日志
     * @return
     */
    public static HttpResponse delete(String url, boolean printLog) {
        //定义请求体
        HttpRequest request = new HttpRequest();
        request.setMethod(HttpMethod.DELETE);
        request.setUrl(url);

        return request(request, printLog);
    }

    /**
     * DELETE删除请求
     *
     * @param url      请求地址
     * @param timeout  超时时间
     * @param printLog 是否打印日志
     * @return
     */
    public static HttpResponse delete(String url, int timeout, boolean printLog) {
        //定义请求体
        HttpRequest request = new HttpRequest();
        request.setMethod(HttpMethod.DELETE);
        request.setUrl(url);
        request.setTimeout(timeout);

        return request(request, printLog);
    }

    /**
     * DELETE删除请求
     *
     * @param url       请求地址
     * @param basicAuth basic认证
     * @param timeout   超时时间
     * @param printLog  是否打印日志
     * @return
     */
    public static HttpResponse delete(String url, BasicAuthorization basicAuth, int timeout, boolean printLog) {
        //定义请求体
        HttpRequest request = new HttpRequest();
        request.setMethod(HttpMethod.DELETE);
        request.setUrl(url);
        request.setBasicAuth(basicAuth);
        request.setTimeout(timeout);

        return request(request, printLog);
    }

    /**
     * DELETE删除请求
     *
     * @param url       请求地址
     * @param headers   请求头
     * @param basicAuth basic认证
     * @param timeout   超时时间
     * @param printLog  是否打印日志
     * @return
     */
    public static HttpResponse delete(String url, Map<String, String> headers, BasicAuthorization basicAuth,
                                      int timeout, boolean printLog) {
        //定义请求体
        HttpRequest request = new HttpRequest();
        request.setMethod(HttpMethod.DELETE);
        request.setUrl(url);
        request.setHeaders(headers);
        request.setBasicAuth(basicAuth);
        request.setTimeout(timeout);

        return request(request, printLog);
    }

    /**
     * 请求获取返回值
     * 若使用body发送数据,则只支持POST/PUT/PATCH
     *
     * @param httpRequest 请求信息
     * @param printLog    是否输出log
     * @return
     */
    private static HttpResponse request(HttpRequest httpRequest, boolean printLog) {
        //记录请求开始时间
        long startTime = System.currentTimeMillis();

        // HttpClientBuilder
        HttpClientBuilder httpBuilder = HttpClientBuilder.create();

        // 配置认证
        if (httpRequest.getBasicAuth() != null) {
            CredentialsProvider credsProvider = new BasicCredentialsProvider();
            credsProvider.setCredentials(new AuthScope(AuthScope.ANY),
                    new UsernamePasswordCredentials(httpRequest.getBasicAuth().getUsername(),
                            httpRequest.getBasicAuth().getPassword()));
            httpBuilder.setDefaultCredentialsProvider(credsProvider);
        }

        // 设置请求配置
        RequestConfig.Builder builder = RequestConfig.custom();

        // 配置超时
        if (httpRequest.getTimeout() > 0) {
            builder.setConnectionRequestTimeout(httpRequest.getTimeout());
            builder.setConnectTimeout(httpRequest.getTimeout());
            builder.setSocketTimeout(httpRequest.getTimeout());
        }

        // 构建请求配置
        RequestConfig requestConfig = builder.build();
        httpBuilder.setDefaultRequestConfig(requestConfig);

        // HttpClient
        CloseableHttpClient closeableHttp = httpBuilder.build();

        // 定义HttpUriRequest
        HttpRequestBase request = getHttpUriRequest(httpRequest);
        request.setConfig(requestConfig);

        // RequestConfig config = request.getConfig();
        // System.out.println("getConnectTimeout=" + config.getConnectTimeout());
        // System.out.println("getConnectionRequestTimeout=" + config.getConnectionRequestTimeout());
        // System.out.println("getSocketTimeout=" + config.getSocketTimeout());

        //定义响应信息
        HttpResponse response = new HttpResponse();

        // 执行get请求
        CloseableHttpResponse closeableHttpResponse = null;
        try {
            // 执行get请求
            closeableHttpResponse = closeableHttp.execute(request);

            // 获取请求状态
            response.setCode(closeableHttpResponse.getStatusLine().getStatusCode());

            //如果需要返回内容，则继续解析
            if (httpRequest.getReturnType() == ReturnType.CONTENT) {
                HttpEntity entity = closeableHttpResponse.getEntity();
                if (entity != null) {
                    response.setContent(EntityUtils.toString(entity, CHARSET));
                }
            }

            //输出log
            if (printLog) {
                log.info("Http Request Success: method={},url={},timeout={},code={},spend={}",
                        httpRequest.getMethod(), httpRequest.getUrl(), httpRequest.getTimeout(),
                        response.getCode(), System.currentTimeMillis() - startTime);
            }

            return response;
        } catch (Exception e) {
            //输出log
            log.error("Http Request Failure: method={},url={},timeout={},spend={},cause={}",
                    httpRequest.getMethod(), httpRequest.getUrl(), httpRequest.getTimeout(),
                    System.currentTimeMillis() - startTime, e.getMessage());

            // 异常则直接终止request
            if (request != null) {
                request.abort();
            }

            //重置响应信息
            response.setCode(0);
            response.setContent(null);

            return response;
        } finally {
            try {
                // 释放链接
                if (request != null) {
                    request.releaseConnection();
                }

                // 关闭CloseableHttpResponse
                if (closeableHttpResponse != null) {
                    // 消耗实体，因为EntityUtils.toString已经关闭了流，但是获取状态码时没有关闭，因此统一再关闭一次
                    EntityUtils.consumeQuietly(closeableHttpResponse.getEntity());
                    closeableHttpResponse.close();
                }

                // 关闭连接CloseableHttpClient,关闭流并释放资源,经测试必须也同时关闭request.abort();和response.close();否则有时会出现连接卡死不动
                closeableHttp.close();
            } catch (Exception e) {
                //输出log
                log.error("Http Request Close Failure: method={},url={},cause={}",
                        httpRequest.getMethod(), httpRequest.getUrl(), e.getMessage());
            }
        }
    }

    /**
     * 定义获取HttpRequestBase
     * 如果服务器需要通过HTTPS连接,那只需要将下面URL中的http换成https
     *
     * @param httpRequest 请求信息
     * @return
     */
    protected static HttpRequestBase getHttpUriRequest(HttpRequest httpRequest) {
        //定义请求体
        HttpRequestBase request = null;
        if (httpRequest.getMethod() == HttpMethod.GET) {
            request = new HttpGet(httpRequest.getUrl());
        } else if (httpRequest.getMethod() == HttpMethod.HEAD) {
            request = new HttpHead(httpRequest.getUrl());
        } else if (httpRequest.getMethod() == HttpMethod.OPTIONS) {
            request = new HttpOptions(httpRequest.getUrl());
        } else if (httpRequest.getMethod() == HttpMethod.TRACE) {
            request = new HttpTrace(httpRequest.getUrl());
        } else if (httpRequest.getMethod() == HttpMethod.DELETE) {
            request = new HttpDelete(httpRequest.getUrl());
        } else if (httpRequest.getMethod() == HttpMethod.POST || httpRequest.getMethod() == HttpMethod.PUT || httpRequest.getMethod() == HttpMethod.PATCH) {
            HttpEntityEnclosingRequestBase entityRequest = null;
            if (httpRequest.getMethod() == HttpMethod.POST) {
                entityRequest = new HttpPost(httpRequest.getUrl());
            } else if (httpRequest.getMethod() == HttpMethod.PUT) {
                entityRequest = new HttpPut(httpRequest.getUrl());
            } else if (httpRequest.getMethod() == HttpMethod.PATCH) {
                entityRequest = new HttpPatch(httpRequest.getUrl());
            }

            //设置请求体,"POST/PUT/PATCH"支持setEntity，填充body
            if (entityRequest != null && httpRequest.getBody() != null) {
                entityRequest.setEntity(new StringEntity(httpRequest.getBody(), CHARSET));
            }

            request = entityRequest;
        }

        //设置通用配置
        if (request != null) {
            //设置请求头
            if (httpRequest.getHeaders() != null && httpRequest.getHeaders().size() > 0) {
                for (Map.Entry<String, String> header : httpRequest.getHeaders().entrySet()) {
                    request.setHeader(header.getKey(), header.getValue());
                }
            }
        }

        return request;
    }

//    /**
//     * 定义Http Body实体
//     * 废弃原因：写的太复杂，没必要
//     * @param content 字符串形式的body
//     * @return
//     */
//    private static StringEntity defineStringEntity(String content) {
//        StringEntity entity = null;
//        if (content != null) {
//            entity = new StringEntity(content, CHARSET);
//            // if (type.equals("json")) {
//            // entity.setContentType("text/json");
//            // }
//            // entity.setContentEncoding(new BasicHeader(HTTP.CONTENT_ENCODING,
//            // HTTP.UTF_8));
//        }
//
//        return entity;
//    }

//    /**
//     * 定义Http Body实体
//     * 废弃原因：此方式不能定义有效的body实体，而被定义为url参数追加方式
//     * @param bodyParamMap key-value形式的body
//     * @return
//     */
//    private static StringEntity defineMapEntity(Map<String, String> bodyParamMap) {
//        UrlEncodedFormEntity entity = null;
//        if (bodyParamMap != null && bodyParamMap.size() > 0) {
//            // 定义实体参数
//            List<NameValuePair> entityParam = new ArrayList<>();
//            for (Map.Entry<String, String> paramMap : bodyParamMap.entrySet()) {
//                entityParam.add(new BasicNameValuePair(paramMap.getKey(), paramMap.getValue()));
//            }
//
//            // 统一使用UTF-8进行编码
//            try {
//                entity = new UrlEncodedFormEntity(entityParam, CHARSET);
//            } catch (UnsupportedEncodingException e) {
//                entity = null;
//            }
//        }
//
//        return entity;
//    }
}

